#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <string>
using namespace std;

const int val = 5;

template <class T>

class blockqueue
{
public:
    blockqueue(const int data = val) : _data(data)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond_sumer, nullptr);
        pthread_cond_init(&_cond_productor, nullptr);
    }
    void push(T &in) // 生产者push
    {

        // 符合条件才能生产
        pthread_mutex_lock(&_mutex);
        while (isfull()) // 只能在临界区内部，判断临界区资源是否就绪，进入了就一定有锁
        {
            // 线程等待的时候，不能持有锁
            // 注定了pthread_cond_wait有释放锁的功能
            pthread_cond_wait(&_cond_productor, &_mutex);
            // 线程唤醒后，注定了从临界区内部继续运行！因为是在临界区切走的（在哪切走就在哪里继续运行）
            // 唤醒前会申请锁，申请成功就马上返回运行
        }
        q.push(in);

        pthread_cond_signal(&_cond_sumer); // 插入数据就可以通知消费者了
        pthread_mutex_unlock(&_mutex);
    }

    void pop(T *out)
    {
        pthread_mutex_lock(&_mutex);

        while (isempty())
        {

            pthread_cond_wait(&_cond_sumer, &_mutex); //
        }
        *out = q.front();
        q.pop();

        pthread_cond_signal(&_cond_productor);
        pthread_mutex_unlock(&_mutex);
    }

    bool isfull() // 是否满
    {
        return q.size() == _data;
    }
    bool isempty() // 是否空
    {
        return q.empty();
    }

    ~blockqueue() // 当作用域消失后马上调用解锁
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond_productor);
        pthread_cond_destroy(&_cond_sumer);
    }

private:
    // 为什么是同一把锁？因为访问的是同一个queue
    queue<T> q;
    int _data; // 数据容量
    pthread_mutex_t _mutex;
    pthread_cond_t _cond_sumer;     // 消费者
    pthread_cond_t _cond_productor; // 生产者
};